import requests
import yaml
from responses_validator import validator

from . import utils, var_handler, extract

def send_request(yaml_file: str, step: str, index: int, subtest: dict, local_vars: dict, global_vars: dict) -> tuple[requests.Response | None, str | None]:
    req_data: dict|None = subtest.get('request')
    if type(req_data) != dict:
        return None, f'{yaml_file}:{step}.{index}.request is invalid'
    if not req_data.get('url'):
        return None, f'{yaml_file}:{step}.{index}.request.url is invalid'
    # todo 这里可能出现异常，后面再处理
    req_data = var_handler.handle(req_data, local_vars, global_vars)
    try:
        response = requests.request(**req_data)
    except Exception as e:
        return None, f"{yaml_file}:{step}.{index} send request failed: {e}"

    return response, None


def assert_response(yaml_file: str, step: str, index: int, subtest: dict, response: requests.Response, local_vars: dict, global_vars: dict) -> list[str]:
    assert_rules: dict|None = subtest.get('response')
    if type(assert_rules) != dict:
        return []
    # todo 这里可能出现异常，后面再处理
    assert_rules = var_handler.handle(assert_rules, local_vars, global_vars)
    # todo 这里可能出现异常，后面再处理
    error_dict = validator(response, **assert_rules, raise_exception=False)
    return list(error_dict.values())


def run(yaml_file: str, global_vars: dict) -> list[dict]:
    """
    执行用例某个步骤

    :param yaml_file: 用例文件名
    :param global_vars: 全局变量
    :return: 执行信息
    """

    # 定义本地变量
    local_vars = {}
    # 加载yaml数据
    with open(yaml_file, 'r', encoding='utf-8') as file:
        data: dict = yaml.safe_load(file)
    # 加载本地变量
    if data.get('vars') is not None:
        local_vars = data['vars']

    results = []
    index = 0
    have_error = False

    for step in ('setup', 'test', 'teardown'):
        step_data = data.get(step)
        if step_data is None:
            continue

        for subtest in step_data:
            result = {
                'file': yaml_file,
                'step': step,
                'index': index,
                'name': subtest.get('name'),
                'status': 'idle', # idle skip fail pass
                'errors': [],
                'request': None,
                'response': None,
            }
            index += 1
            results.append(result)

            if step != 'teardown' and have_error:
                result['status'] = 'skip'
                continue

            # 发送请求获取响应
            response, error = send_request(yaml_file, step, result['index'], subtest, local_vars, global_vars)
            if error is not None:
                result['status'] = 'fail'
                result['errors'].append(error)
                have_error = True
                continue
            result['request'] = {
                'url': response.request.url,
                'method': response.request.method,
                'headers': dict(response.request.headers),
                'body': utils.safe_request_body(response.request),
            }
            result['response'] = {
                'status_code': response.status_code,
                'headers': dict(response.headers),
                'body': utils.safe_response_body(response),
            }

            # 断言响应
            errors = assert_response(yaml_file, step, result['index'], subtest, response, local_vars, global_vars)
            if len(errors) > 0:
                result['status'] = 'fail'
                result['errors'].extend(errors)
                have_error = True
                continue

            # 变量提取
            extract_data = subtest.get('extract')
            if type(extract_data) == dict:
                try:
                    local_vars.update(extract.extract_variables(response, extract_data))
                except Exception as e:
                    result['status'] = 'fail'
                    result['errors'].append(f"{yaml_file}:{step}.{result['index']} extract vars failed: {e}")
                    have_error = True
                    continue

            result['status'] = 'pass'

    return results
